
// ===============================================================================================================
// -*- C++ -*-
//
// ProgressiveMesh.cpp - Progressive Mesh implementation, based on the algorithms presented here:
// http://www.melax.com/polychop - By Stan Melax.
//
// Copyright (c) 2010 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include "ProgressiveMesh.hpp"

// == Class ProgressiveMesh ==

ProgressiveMesh::ProgressiveMesh(const Mesh * pMesh, int maxVerts, float LOD, float morph)
	: m_MaxVerts(maxVerts)
	, m_LodBase(LOD)
	, m_Morph(morph)
	, m_CollapseMap()
	, m_ScaleFactor(1.0f)
	, m_RenderMode(Mesh::Solid)
	, m_Triangles()
	, m_Vertices()
{
	// Copy vertices:
	if (pMesh->VertexCount() > 0)
	{
		m_Vertices.resize(pMesh->VertexCount());
		void * p = reinterpret_cast<void *>(m_Vertices.data());
		memcpy(p, pMesh->Vertices(), pMesh->VertexCount() * sizeof(Vec3));
	}

	// Copy triangle indices:
	Mesh::GroupMap::const_iterator Ptr = pMesh->Groups().begin();
	Mesh::GroupMap::const_iterator End = pMesh->Groups().end();

	while (Ptr != End)
	{
		unsigned int nFaces = (*Ptr).second->vFaces.size();

		for (unsigned int i = 0; i < nFaces; i++)
		{
			m_Triangles.push_back(Triangle((*Ptr).second->vFaces[i]->pVertexIndex));
		}

		++Ptr;
	}

	Array<int> Permutation;
	MeshOptimize(m_Vertices, m_Triangles, m_CollapseMap, Permutation);
	PermuteVertices(Permutation);
}

void ProgressiveMesh::Render() const
{
	glScalef(m_ScaleFactor, m_ScaleFactor, m_ScaleFactor);

	unsigned int TriCount = m_Triangles.size();

	while (TriCount--)
	{
		// Get the correct mesh, based on the level of detail:

		const Triangle & Tri = m_Triangles[TriCount];

		int p0 = MapLookup(Tri.VertexIndex[0], m_MaxVerts);
		int p1 = MapLookup(Tri.VertexIndex[1], m_MaxVerts);
		int p2 = MapLookup(Tri.VertexIndex[2], m_MaxVerts);

		if ((p0 == p1) || (p1 == p2) || (p2 == p0))
			continue;

		int q0 = MapLookup(p0, (int)(m_MaxVerts * m_LodBase));
		int q1 = MapLookup(p1, (int)(m_MaxVerts * m_LodBase));
		int q2 = MapLookup(p2, (int)(m_MaxVerts * m_LodBase));

		const Vec3 & v0 = m_Vertices[p0]*m_Morph + m_Vertices[q0]*(1.0f-m_Morph);
		const Vec3 & v1 = m_Vertices[p1]*m_Morph + m_Vertices[q1]*(1.0f-m_Morph);
		const Vec3 & v2 = m_Vertices[p2]*m_Morph + m_Vertices[q2]*(1.0f-m_Morph);

		glBegin((m_RenderMode == Mesh::Solid) ? GL_TRIANGLES : (m_RenderMode == Mesh::Wireframe) ? GL_LINE_STRIP : GL_POINTS);

		// Compute face normal:
		Vec3 normal((v1-v0).CrossProduct(v2-v1));

		normal.Normalize();

		glNormal3fv(normal.v);

		// Draw the triangle:
		glVertex3fv(v0.v);
		glVertex3fv(v1.v);
		glVertex3fv(v2.v);

		glEnd();
	}
}

int ProgressiveMesh::MapLookup(int x, int y) const
{
	if (y <= 0)
	{
		return (0);
	}

	while (x >= y)
	{
		x = m_CollapseMap[x];
	}

	return (x);
}

void ProgressiveMesh::PermuteVertices(const Array<int> & Permutation)
{
	unsigned int vertexCount = m_Vertices.size();

	if (Permutation.size() >= vertexCount)
	{
		unsigned int i;

		Vec3 * pTmpVerts = new Vec3 [vertexCount];

		memcpy(pTmpVerts, m_Vertices.data(), vertexCount * sizeof(Vec3));

		for (i = 0; i < vertexCount; i++)
		{
			memcpy(&m_Vertices[Permutation[i]], &pTmpVerts[i], sizeof(Vec3));
		}

		unsigned int triCount = m_Triangles.size();

		for (i = 0; i < triCount; i++)
		{
			Triangle & Tri = m_Triangles[i];

			Tri.VertexIndex[0] = Permutation[Tri.VertexIndex[0]];
			Tri.VertexIndex[1] = Permutation[Tri.VertexIndex[1]];
			Tri.VertexIndex[2] = Permutation[Tri.VertexIndex[2]];
		}

		delete[] pTmpVerts;
	}
}

bool ProgressiveMesh::SavePMeshToWavefrontObject(const std::string & filename) const
{
	// Write progressive mesh to file, with the OBJ formatting:

	unsigned int i, TriCount, VertCount;

	FILE * fp = fopen(filename.c_str(), "wt");

	if (fp != NULL)
	{
		fputc('\n', fp);
		fprintf(fp, "# This mesh was generated by the Progressive Mesh Algorithm.\n");
		fputc('\n', fp);

		TriCount = m_Triangles.size();

		Array<Triangle> TempTris;
		Array<Vec3> TempVerts;

		for (i = 0; i < TriCount; i++)
		{
			// Get the correct mesh, based on the level of detail:

			Triangle Tri;
			memcpy(&Tri, &m_Triangles[i], sizeof(Triangle));

			int p0 = MapLookup(Tri.VertexIndex[0], m_MaxVerts);
			int p1 = MapLookup(Tri.VertexIndex[1], m_MaxVerts);
			int p2 = MapLookup(Tri.VertexIndex[2], m_MaxVerts);

			if ((p0 == p1) || (p1 == p2) || (p2 == p0))
				continue;

			int q0 = MapLookup(p0, (int)(m_MaxVerts * m_LodBase));
			int q1 = MapLookup(p1, (int)(m_MaxVerts * m_LodBase));
			int q2 = MapLookup(p2, (int)(m_MaxVerts * m_LodBase));

			Vec3 TriangleVerts[3] =
			{
				Vec3(m_Vertices[p0]*m_Morph + m_Vertices[q0]*(1.0f-m_Morph)),
				Vec3(m_Vertices[p1]*m_Morph + m_Vertices[q1]*(1.0f-m_Morph)),
				Vec3(m_Vertices[p2]*m_Morph + m_Vertices[q2]*(1.0f-m_Morph)),
			};

			for (unsigned int j = 0; j < 3; j++)
			{
				if (TempVerts.add_unique(TriangleVerts[j]) == 0)
				{
					Tri.VertexIndex[j] = TempVerts.size()-1;
				}
				else
				{
					unsigned int n = TempVerts.size();

					while (n--)
					{
						if (TempVerts[n] == TriangleVerts[j])
						{
							Tri.VertexIndex[j] = n;
							break;
						}
					}
				}
			}

			TempTris.add_unique(Tri);
		}

		VertCount = TempVerts.size();

		for (i = 0; i < VertCount; i++)
		{
			const Vec3 & Vert = TempVerts[i];

			fprintf(fp, "v %f %f %f\n", Vert.x, Vert.y, Vert.z);
		}

		fprintf(fp, "# %i Vertices.\n", i);

		fprintf(fp, "g MainGroup\n");

		TriCount = TempTris.size();

		for (i = 0; i < TriCount; i++)
		{
			const Triangle & Tri = TempTris[i];

			fprintf(fp, "f %u %u %u\n", Tri.VertexIndex[0]+1, Tri.VertexIndex[1]+1, Tri.VertexIndex[2]+1);
		}

		fprintf(fp, "# %i Triangles.\n", i);

		fclose(fp);

		return (true);
	}

	return (false);
}

bool ProgressiveMesh::Good() const
{
	return ((m_Vertices.size() > 0) && (m_Triangles.size() > 0));
}

void ProgressiveMesh::SetScale(float s)
{
	m_ScaleFactor = s;
}

void ProgressiveMesh::SetRenderMode(const Mesh::RenderMode m)
{
	m_RenderMode = m;
}

void ProgressiveMesh::SetLOD(float newLOD)
{
	m_LodBase = newLOD;
}

void ProgressiveMesh::SetMaxVertices(int newMax)
{
	m_MaxVerts = newMax;
}

void ProgressiveMesh::SetMorph(float newMorph)
{
	m_Morph = newMorph;
}

ProgressiveMesh::~ProgressiveMesh()
{
	m_Vertices.clear();
	m_Triangles.clear();
	m_CollapseMap.clear();
}
